#include "ws2812b.h"
#include "delay.h"
#include "do_mqtt.h"
#include "w55mh32_tim.h"
#include <string.h>
/* Hardware configuration */
#define WS2812B_TIM         TIM1
#define WS2812B_TIM_CHANNEL TIM_Channel_1
#define WS2812B_GPIO_PORT   GPIOA
#define WS2812B_GPIO_PIN    GPIO_Pin_8

/* Static variable declarations */

static uint16_t s_pwm_period = 0;        // PWM period value
static uint16_t s_bit0_duty = 0;         // Duty cycle for '0' code
static uint16_t s_bit1_duty = 0;         // Duty cycle for '1' code
static uint8_t s_led_colors[LED_NUM][3]; // LED color data buffer

/**
 * @brief Initialize GPIO and timer for WS2812B
 *
 * @note Configure PWM output to meet WS2812B timing requirements (800kHz)
 */
void ws2812b_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};
    TIM_TimeBaseInitTypeDef tim_base_init_struct = {0};
    TIM_OCInitTypeDef tim_oc_init_struct = {0};
    TIM_BDTRInitTypeDef tim_bdtr_init_struct = {0};

    /* 1. Enable peripheral clocks */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);

    /* 2. Configure GPIO (PA8 as TIM1_CH1 PWM output) */
    gpio_init_struct.GPIO_Pin = WS2812B_GPIO_PIN;
    gpio_init_struct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(WS2812B_GPIO_PORT, &gpio_init_struct);

    /* 3. Get system clock frequency, calculate PWM parameters */
    RCC_ClocksTypeDef rcc_clocks = {0};
    RCC_GetClocksFreq(&rcc_clocks);
    const uint32_t timer_clock = rcc_clocks.PCLK2_Frequency;

    s_pwm_period = (timer_clock / 800000) - 1; // Target frequency 800kHz
    s_bit0_duty =
        (uint16_t)(0.40e-6 * timer_clock); // Pulse width for '0' (0.4us)
    s_bit1_duty =
        (uint16_t)(0.80e-6 * timer_clock); // Pulse width for '1' (0.8us)

    /* 4. Configure timer base */
    tim_base_init_struct.TIM_Period = s_pwm_period;
    tim_base_init_struct.TIM_Prescaler = 0;
    tim_base_init_struct.TIM_ClockDivision = 0;
    tim_base_init_struct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(WS2812B_TIM, &tim_base_init_struct);

    /* 5. Configure PWM output mode */
    tim_oc_init_struct.TIM_OCMode = TIM_OCMode_PWM1;
    tim_oc_init_struct.TIM_OutputState = TIM_OutputState_Enable;
    tim_oc_init_struct.TIM_OCPolarity = TIM_OCPolarity_High;
    tim_oc_init_struct.TIM_Pulse = 0;
    TIM_OC1Init(WS2812B_TIM, &tim_oc_init_struct);

    /* 6. Configure BDTR register for advanced timer */
    TIM_BDTRStructInit(&tim_bdtr_init_struct);
    tim_bdtr_init_struct.TIM_OSSRState = TIM_OSSRState_Enable;
    tim_bdtr_init_struct.TIM_OSSIState = TIM_OSSIState_Enable;
    tim_bdtr_init_struct.TIM_LOCKLevel = TIM_LOCKLevel_1;
    tim_bdtr_init_struct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
    TIM_BDTRConfig(WS2812B_TIM, &tim_bdtr_init_struct);

    /* 7. Enable timer and PWM outputs */
    TIM_CtrlPWMOutputs(WS2812B_TIM, ENABLE);
    TIM_Cmd(WS2812B_TIM, ENABLE);

    /* 8. Initialize all LEDs to off state */
    ws2812b_clear_all();
}

/**
 * @brief Set color for specific LED
 *
 * @param index LED index (0-based)
 * @param red Red component (0-255)
 * @param green Green component (0-255)
 * @param blue Blue component (0-255)
 *
 * @note Color data stored in buffer, requires ws2812b_update() to take effect
 */
void ws2812b_set_color(uint8_t index, uint8_t red, uint8_t green, uint8_t blue)
{
    if (index < LED_NUM)
    {
        s_led_colors[index][0] = green; // WS2812B uses GRB format
        s_led_colors[index][1] = red;
        s_led_colors[index][2] = blue;
    }
}

/**
 * @brief Set all LEDs to the same color
 *
 * @param red Red component (0-255)
 * @param green Green component (0-255)
 * @param blue Blue component (0-255)
 *
 * @note Color data stored in buffer, requires ws2812b_update() to take effect
 */
void ws2812b_set_all_color(uint8_t red, uint8_t green, uint8_t blue)
{
    for (uint8_t i = 0; i < LED_NUM; i++)
    {
        ws2812b_set_color(i, red, green, blue);
    }
}

/**
 * @brief Turn off all LEDs (set to black)
 *
 * @note Requires ws2812b_update() to take effect
 */
void ws2812b_clear_all(void) { memset(s_led_colors, 0, sizeof(s_led_colors)); }

/**
 * @brief Send color data from buffer to WS2812B strip
 *
 * @note Strictly follows WS2812B timing requirements, sends GRB format data
 */
void ws2812b_update(void)
{
    /* Disable interrupts to ensure precise timing */
    __disable_irq();

    TIM_TypeDef *timer_ptr = WS2812B_TIM;
    timer_ptr->CCR1 = 0; // Initialize to low level

    /* Process all LEDs */
    for (uint8_t i = 0; i < LED_NUM; i++)
    {
        /* Combine 24-bit GRB color data (Green 8-bit + Red 8-bit + Blue 8-bit) */
        uint32_t grb = ((s_led_colors[i][0] << 16) | (s_led_colors[i][1] << 8) |
                        s_led_colors[i][2]);

        /* Send each bit from MSB to LSB */
        for (int8_t bit_pos = 23; bit_pos >= 0; bit_pos--)
        {
            /* Set PWM duty cycle based on current bit value */
            uint16_t duty = (grb & (1UL << bit_pos)) ? s_bit1_duty : s_bit0_duty;
            timer_ptr->CCR1 = duty;

            /* Wait for one PWM period */
            uint32_t start_time = timer_ptr->CNT;
            while ((timer_ptr->CNT - start_time) < s_pwm_period)
                ;
        }
    }

    /* Send reset signal (at least 50us low level) */
    timer_ptr->CCR1 = 0;

    /* Precise delay for 50us */
    const uint32_t reset_cycles =
        (SystemCoreClock / 20000); // Clock cycles for 50us
    for (volatile uint32_t i = 0; i < reset_cycles; i++)
        ;

    /* Restore interrupts */
    __enable_irq();
}

/**
 * @brief Rainbow animation effect
 *
 * @note Cyclically updates LED colors to create flowing rainbow effect
 */
void ws2812b_rainbow_effect(void)
{
    static uint8_t start_position = 0;

    for (uint8_t i = 0; i < LED_NUM; i++)
    {
        const uint8_t position = (start_position + i) % 256;
        uint8_t r = 0;
        uint8_t g = 0;
        uint8_t b = 0;

        /* Calculate RGB values based on position */
        if (position < 85)
        {
            r = position * 3;
            g = 255 - position * 3;
        }
        else if (position < 170)
        {
            const uint8_t adjusted_pos = position - 85;
            r = 255 - adjusted_pos * 3;
            b = adjusted_pos * 3;
        }
        else
        {
            const uint8_t adjusted_pos = position - 170;
            g = adjusted_pos * 3;
            b = 255 - adjusted_pos * 3;
        }

        ws2812b_set_color(i, r, g, b);
    }

    start_position = (start_position + 5) % 256; // Update start position
    ws2812b_update();
    delay_ms(400);
}

/**
 * @brief Running light effect
 *
 * @note Single LED lights up sequentially in a circular pattern
 */
void ws2812b_running_light(void)
{
    static uint8_t position = 0;

    ws2812b_clear_all();
    ws2812b_set_color(position, get_red_value(), get_green_value(), get_blue_value());
    ws2812b_update();
    delay_ms(200);

    position = (position + 1) % LED_NUM;
}

/* Getter function implementations */
uint8_t get_blue_value(void)
{
    return g_blue_value;
}

uint8_t get_red_value(void)
{
    return g_red_value;
}

uint8_t get_green_value(void)
{
    return g_green_value;
}
